package com.example.demo;

import java.io.File;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Sheet;

/**
 *	Excel 解析工具类（解析为 JavaBean 对象）
 * 
 * @author <a href="mailto:wei.feng@vtradex.com">wei.feng</a>
 * @since 2019-05-19 10:14
 */
public class ExcelParseHelper {
	
	/**
	 *	将 Excel 解析为 JavaBean 对象
	 * 
	 * @since 2019-04-23 16:07
	 * @param file
	 * @param clazz
	 * @param rowIndex       从第几行开始解析，第一行为 0，依次类推
	 * @return
	 */
	public static <T> List<T> parse(File file, Class<T> clazz, int rowIndex) {
		List<List<String>> resultList = parse(file, rowIndex);
		
		int currentRow = 1 + rowIndex;
		List<T> list = new ArrayList<>();
		for(List<String> rowDatas : resultList) {
			list.add(setField(clazz, rowDatas, currentRow));
			currentRow++;
		}
		return list;
	}

	/**
	 *	为对象的每一个属性赋值
	 * 
	 * @since 2019-05-19 10:20
	 * @param <T>
	 * @param clazz
	 * @param rowDatas 当前行的数据
	 * @return
	 */
	private static <T> T setField(Class<T> clazz, List<String> rowDatas, int currentRow) {
		try {
			T obj = clazz.newInstance();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
				int index = excelProperty.index();
				String format = excelProperty.format();
				String[] nvl = excelProperty.nvl();
				boolean required = excelProperty.required();
				Object value = getFieldValue(field, index, rowDatas.get(index), required, format, nvl, currentRow);
				field.setAccessible(true);
				field.set(obj, value);
			}
			return obj;
		} catch (InstantiationException | IllegalAccessException e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 *	将 Excel 解析为 List 集合
	 * 
	 * @since 2019-05-19 10:20
	 * @param file
	 * @param rowIndex 从第几行开始解析，第一行为 0，依次类推
	 * @return
	 */
	public static List<List<String>> parse(File file, int rowIndex) {
		Sheet sheet = PoiUtils.parseFile(file);
		int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
		int cellCount = sheet.getRow(rowIndex).getPhysicalNumberOfCells(); // 总列数

		List<List<String>> result = new ArrayList<List<String>>();
		for (int i = rowIndex; i < rowCount; i++) {
			List<String> cellResult = new ArrayList<>();
			for (int j = 0; j < cellCount; j++) {
				cellResult.add(PoiUtils.getCellValue(sheet, i, j));
			}
			result.add(cellResult);
		}
		return result;
	}

	/**
	 *	根据 Java 对象的属性的类型，将 Excel 中的值解析为相对应的类型
	 * 
	 * @since 2019-05-19 10:20
	 * @param field    对象的属性
	 * @param index	          对应 Excel 的第几列
	 * @param value    Excel 中的值
	 * @param required 是否必填
	 * @param pattern  如果 Java 对象的属性的类型为 Date，使用 pattern 模式将 Excel 中的值转换为 Date 类型
	 * @param nvl	          将 Excel 中的字段转换为Java对象的值，eg：打开 → OP
	 * @param currentRow 当前为 Excel 第几行
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static Object getFieldValue(Field field, int index, String value, boolean required, String pattern, String[] nvl, int currentRow) {
		Class typeClass = field.getType();
		Object val = value;
		try {
			if(required && isEmpty(value)) {
				throw new IllegalArgumentException(String.format("第【%s】行【%s】列，信息不能为空", currentRow, index +1));
			}
			
			if (typeClass == Short.class) {
				val = Short.valueOf(isEmpty(value) ? "0" : value);
			} else if (typeClass == Integer.class) {
				val = Integer.valueOf(isEmpty(value) ? "0" : value);
			} else if (typeClass == Long.class) {
				val = Long.valueOf(isEmpty(value) ? "0" : value);
			} else if (typeClass == Float.class) {
				val = Float.valueOf(isEmpty(value) ? "0" : value);
			} else if (typeClass == Double.class) {
				val = Double.valueOf(isEmpty(value) ? "0" : value);
			} else if (typeClass == Date.class) {
				val = parseDate(value, pattern);
			} else if (typeClass == Character.class) {
				val = Character.valueOf(value.charAt(0));
			} else if (typeClass == Boolean.class) {
				val = Arrays.asList(BooleanEnum.values()).stream()
						.filter(x -> x.getKey().equals(value.toUpperCase()))
						.findFirst()
						.orElseThrow(() -> new IllegalArgumentException(String.format("第【%s】行【%s】列，不能识别的值【%s】", currentRow, index +1, value)))
						.getValue();
			} else if(typeClass == String.class && nvl.length > 0) {
				Map<String, String> map = convertToMap(nvl);
				val = map.get(value);
				if(val == null) {
					throw new IllegalArgumentException(String.format("第【%s】行【%s】列，不能识别的值【%s】", currentRow, index +1, value));
				}
			}
		} catch (IllegalArgumentException e) {
			throw new IllegalArgumentException(e.getMessage());
		} catch (Exception e) {
			throw new IllegalArgumentException(String.format("第【%s】行【%s】列，不能识别的值【%s】", currentRow, index +1, value));
		}
		return val;
	}
	
	private static Map<String, String> convertToMap(String[] args) {
		Map<String, String> map = new HashMap<>();
		for (int i = 0; i < args.length - 1; i+=2) {
			map.put(args[i], args[i + 1]);
		}
		return map;
	}
	
	private static boolean isEmpty(String str) {
		return str == null || str.length() == 0;
	}
	
	private static Date parseDate(String dateStr, String pattern) {
		try {
			return new SimpleDateFormat(pattern).parse(dateStr);
		} catch (ParseException e) {
			throw new IllegalArgumentException(String.format("日期转换失败【%s】", dateStr), e);
		}
	}
}
